﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using HalconDotNet;

namespace Kimd.Vision
{
    [Serializable]
    public class Rectangle2NB : ShapeNB, ICloneable
    {
        private HTuple _rowsInit, _rows;
        private HTuple _colsInit, _cols;
        private HHomMat2D _hom2D, _tmp;

        public PointF Center { get; set; }
        public float Length1 { get; set; }
        public float Length2 { get; set; }
        public float Angle { get; set; }
        public PointF ActualCenter { get; set; }
        public double ActualAngle { get; set; }

        public Rectangle2NB()
            : base()
        {
            TypeName = ShapeType.Rectangle2;
            this.Length2 = 60;
            this.Length1 = 100;
            _rowsInit = new HTuple(new double[] {-1.0, -1, 1, 0, 0, -1.0, 1.0,
                                                   1.0,  0.0, 0.0 });
            _colsInit = new HTuple(new double[] {-1.0, 0, 0, -1, 1, 1.0,  1.0,
                                                  -1.0, 0.0, 0.5 });
            _hom2D = new HHomMat2D();
            _tmp = new HHomMat2D();
        }

        private void updateHandlePos()
        {
            _hom2D.HomMat2dIdentity();
            _hom2D = _hom2D.HomMat2dTranslate(new HTuple(Center.X), new HTuple(Center.Y));
            _hom2D = _hom2D.HomMat2dRotateLocal(new HTuple(-Angle));
            _tmp = _hom2D.HomMat2dScaleLocal(new HTuple(Length1), new HTuple(Length2));
            _cols = _tmp.AffineTransPoint2d(_colsInit, _rowsInit, out _rows);
        }
        public override void Draw(HWindow window, Graphics g, Func<PointF, PointF> pixel2Window, Func<float, float> lenConvert, bool disableEdit)
        {
            if (disableEdit || IsShowOnly)
            {
                window.SetColor(PaintColor);
                window.SetLineWidth(LineWidth);
                window.SetDraw("margin");
                window.DispRectangle2(new HTuple(Center.Y), new HTuple(Center.X), new HTuple(Angle), new HTuple(Length1), new HTuple(Length2));
            }
            else
            {
                if (!IsSelected)
                {
                    window.SetColor(PaintColor);
                    window.SetLineWidth(LineWidth);
                    window.SetDraw("margin");
                    window.DispRectangle2(new HTuple(Center.Y), new HTuple(Center.X), new HTuple(Angle), new HTuple(Length1), new HTuple(Length2));
                    window.SetColor("green");
                    window.DispArrow(new HTuple(Center.Y), new HTuple(Center.X), new HTuple(Center.Y + (Math.Sin(-Angle) * Length1 * 1.0)),
                        new HTuple(Center.X + (Math.Cos(-Angle) * Length1 * 1.0)), 2);
                    window.SetColor("yellow");
                    updateHandlePos();
                    float size = lenConvert(5);
                    for (int i = 0; i < _rows.Length; i++)
					{
						if (IsMoveOnly && i < 8)
							continue;
						else
							window.DispRectangle2(_rows[i].D, _cols[i].D, Angle, size, size);
                    }
                }
                else
                {
                    window.SetColor("orange");
                    window.SetLineWidth(1);
                    window.SetDraw("margin");
                    window.DispRectangle2(new HTuple(Center.Y), new HTuple(Center.X), new HTuple(Angle), new HTuple(Length1), new HTuple(Length2));
                    window.SetColor("green");
                    window.DispArrow(new HTuple(Center.Y), new HTuple(Center.X), new HTuple(Center.Y + (Math.Sin(-Angle) * Length1 * 1.0)),
                        new HTuple(Center.X + (Math.Cos(-Angle) * Length1 * 1.0)), 2);
                    window.SetColor("red");
                    updateHandlePos();
                    float size = lenConvert(5);
                    for (int i = 0; i < _rows.Length; i++)
                    {
						if (IsMoveOnly && i < 8)
							continue;
						else
							window.DispRectangle2(_rows[i].D, _cols[i].D, Angle, size, size);
                    }
                }
            }
        }
        public override void Change(PointF point)
        {
            switch (ChangeModel)
            {
                case PositionNB.In:
                    float dx = point.X - Center.X;
                    float dy = point.Y - Center.Y;
                    this.Center = new PointF(Center.X + dx, Center.Y + dy);
					if (NotifyShapeChange != null && !this.IsShowOnly)
						NotifyShapeChange(PositionNB.In, this);
                    break;
                case PositionNB.LeftUp:
                case PositionNB.RightUp:
                case PositionNB.LeftDown:
                case PositionNB.RightDown:
                    double angle = Math.Atan(this.Length2 / this.Length1);
                    this.Length1 = DistancePp(point, this.Center) * (float)Math.Cos(angle);
                    this.Length2 = DistancePp(point, this.Center) * (float)Math.Sin(angle);
                    break;
                case PositionNB.Left:
                case PositionNB.Right:
                    this.Length1 = DistancePp(point, this.Center);
                    break;
                case PositionNB.Up:
                case PositionNB.Down:
                    this.Length2 = DistancePp(point, this.Center);
                    break;
                case PositionNB.Angle:
                    float dist = DistancePp(point, this.Center);
                    if (point.X >= this.Center.X && dist != 0)
                        this.Angle = (float)(Math.Asin((double)((-point.Y + this.Center.Y) / dist)));
                    else
                    {
                        if (point.Y <= this.Center.Y && dist != 0)
                            this.Angle = (float)(Math.PI - (Math.Asin((double)((-point.Y + this.Center.Y) / dist))));
                        else
                            this.Angle = (float)(-1 * Math.PI - (Math.Asin((double)((-point.Y + this.Center.Y) / dist))));
                    }
					if(NotifyShapeChange != null)
						NotifyShapeChange(PositionNB.Angle, this);
					break;
                case PositionNB.Create:
                    dist = DistancePp(point, this.Center);
                    if (point.X >= this.Center.X && dist != 0)
                    {
                        double val = (double)((-point.Y + this.Center.Y) / dist);
                        val = val > 1 ? 1 : val;
                        this.Angle = (float)(Math.Asin(val));
                    }
                    else
                    {
                        if (point.Y <= this.Center.Y && dist != 0)
                        {
                            this.Angle = (float)(Math.PI - (Math.Asin((double)((-point.Y + this.Center.Y) / dist))));
                        }
                        else
                        {
                            this.Angle = (float)(-1 * Math.PI - (Math.Asin((double)((-point.Y + this.Center.Y) / dist))));
                        }
                    }
                    this.Length1 = DistancePp(point, this.Center);
                    break;
            }
        }
        public override HObject ToRegion()
        {
            HRegion reg = new HRegion();
            reg.GenRectangle2((double)Center.Y, (double)Center.X, (double)Angle, (double)Length1, (double)Length2);
            return reg;
        }
        public override PositionNB PointOnShape(PointF testPoint, Func<float, float> lenConvert)
        {
            distance2Handle = (int)lenConvert(10);

            updateHandlePos();
            double dcenter = HMisc.DistancePp(new HTuple(testPoint.Y), new HTuple(testPoint.X), new HTuple(_rows[8].D), new HTuple(_cols[8].D));
            if (dcenter < distance2Handle)
                return PositionNB.In;
            double dleftup = HMisc.DistancePp(new HTuple(testPoint.Y), new HTuple(testPoint.X), new HTuple(_rows[0].D), new HTuple(_cols[0].D));
            if (dleftup < distance2Handle && !IsMoveOnly)
                return PositionNB.LeftUp;
            double dup = HMisc.DistancePp(new HTuple(testPoint.Y), new HTuple(testPoint.X), new HTuple(_rows[1].D), new HTuple(_cols[1].D));
            if (dup < distance2Handle && !IsMoveOnly)
                return PositionNB.Up;
            double drightup = HMisc.DistancePp(new HTuple(testPoint.Y), new HTuple(testPoint.X), new HTuple(_rows[5].D), new HTuple(_cols[5].D));
            if (drightup < distance2Handle && !IsMoveOnly)
                return PositionNB.RightUp;
            double ddown = HMisc.DistancePp(new HTuple(testPoint.Y), new HTuple(testPoint.X), new HTuple(_rows[2].D), new HTuple(_cols[2].D));
            if (ddown < distance2Handle && !IsMoveOnly)
                return PositionNB.Down;
            double dleft = HMisc.DistancePp(new HTuple(testPoint.Y), new HTuple(testPoint.X), new HTuple(_rows[3].D), new HTuple(_cols[3].D));
            if (dleft < distance2Handle && !IsMoveOnly)
                return PositionNB.Left;
            double dright = HMisc.DistancePp(new HTuple(testPoint.Y), new HTuple(testPoint.X), new HTuple(_rows[4].D), new HTuple(_cols[4].D));
            if (dright < distance2Handle && !IsMoveOnly)
                return PositionNB.Right;
            double dleftdown = HMisc.DistancePp(new HTuple(testPoint.Y), new HTuple(testPoint.X), new HTuple(_rows[7].D), new HTuple(_cols[7].D));
            if (dleftdown < distance2Handle && !IsMoveOnly)
                return PositionNB.LeftDown;
            double drightdown = HMisc.DistancePp(new HTuple(testPoint.Y), new HTuple(testPoint.X), new HTuple(_rows[6].D), new HTuple(_cols[6].D));
            if (drightdown < distance2Handle && !IsMoveOnly)
                return PositionNB.RightDown;
            double dangle = HMisc.DistancePp(new HTuple(testPoint.Y), new HTuple(testPoint.X), new HTuple(_rows[9].D), new HTuple(_cols[9].D));
            if (dangle < distance2Handle)
                return PositionNB.Angle;
            return PositionNB.Out;
        }
        public override void ToXmlNode(out Dictionary<string, object> attributes, out Dictionary<string, object> nodes)
        {
            attributes = new Dictionary<string, object>();
            attributes.Add("Color", PaintColor);
            attributes.Add("LineWidth", LineWidth);
            attributes.Add("Type", TypeName.ToString());
            attributes.Add("Operator", RegOperator.ToString());
            nodes = new Dictionary<string, object>();
            nodes.Add("Center", Center.X.ToString("0.000") + "," + Center.Y.ToString("0.000"));
            nodes.Add("Angle", Angle.ToString("0.000"));
            nodes.Add("Length1", Length1.ToString("0.000"));
            nodes.Add("Length2", Length2.ToString("0.000"));
            nodes.Add("Alias", string.IsNullOrEmpty(Alias) ? "" : Alias);
        }
        public override void FromXmlNode(XmlNode node)
        {
            RegOperator = (ShapeOperator)Enum.Parse(typeof(ShapeOperator), node.Attributes["Operator"].Value);
            PaintColor = node.Attributes["Color"].Value;
            LineWidth = Int32.Parse(node.Attributes["LineWidth"].Value);

            foreach (XmlNode xn in node.ChildNodes)
            {
                if (xn.Name == "Center")
                {
                    Center = new PointF(float.Parse(xn.InnerText.Split(',')[0]),
                                        float.Parse(xn.InnerText.Split(',')[1]));
                }
                else if (xn.Name == "Angle")
                {
                    Angle = float.Parse(xn.InnerText);
                }
                else if (xn.Name == "Length1")
                {
                    Length1 = float.Parse(xn.InnerText);
                }
                else if (xn.Name == "Length2")
                {
                    Length2 = float.Parse(xn.InnerText);
                }
                else if (xn.Name == "Alias")
                {
                    Alias = xn.InnerText;
                }
            }
        }
        public override PointF GetCenter()
        {
            return this.Center;
        }
    }
}
